#pragma once

#include <memory>
#include <string>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

const static int g_socket = -1;
const static int g_port = 8080;

using namespace MyLogModule;

using func_t = std::function<std::string(std::string)>;
using addUser_t = std::function<void(InetAddr& id)>;    //添加用户的方法
using router_t = std::function<void (int socket,std::string message)>;
using task_t = std::function<void()>;

using removeUser_t = std::function<void(InetAddr& id)>;    //删除用户的方法


class UdpServer
{
public:
    UdpServer( uint16_t port = g_port)
        : _socket(g_socket)
        , _addr(port)   
        , _isRunning(false)
    {
    }

    ~UdpServer()
    {
        if(_socket > 0)
            close(_socket);
    }

    void RegisterFunc(addUser_t adduser,router_t router,removeUser_t remove)
    {
        _adduser = adduser; //添加用户的方法
        _router = router;   //转发信息的方法
        _remove = remove;   //添加删除用户方法
    }

    void Init()
    {
        // 1. 创建网络socket
        _socket = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_socket == -1)
        {
            LOG(FATAL) << "socket create fail";
            Die(SOCKET_ERR);
        }

        LOG(NORMAL) << "file fd:" << _socket;
        // 2. 向套接字中填充网络信息--无需再做

        // 3. 设置套接字进入内核
        int n = ::bind(_socket, _addr.Getaddr(), _addr.Getlen());
        if (n == -1)
        {
            LOG(FATAL) << "bind fail";
            Die(BIND_ERR);
        }
        LOG(NORMAL) << "bind success";
    }

    void Start()
    {
        if (!_isRunning)
        {
            _isRunning = true;
            while (true)
            {
                //输出型参数
                struct sockaddr_in fromUser;
                socklen_t len = sizeof(fromUser);
                char buffer[1024];
                int n = ::recvfrom(_socket, &buffer, sizeof(buffer)-1, 0, CONVERSE(&fromUser), &len);
                if (n > 0)
                {
                    buffer[n] = 0;
                    InetAddr clientAddr(fromUser);  
                    std::string message;
                    
                    if(strcmp(buffer,"QUIT") == 0)
                    {
                        _remove(clientAddr);
                        message += clientAddr.getIdInfo() + "say: 我走了，你们聊吧~";
                    }
                    else
                    {
                        //添加用户，内部自动判断是否添加
                        _adduser(clientAddr);
                        message += clientAddr.getIdInfo() + "say:" + buffer;
                    }

                    std::string clientinfo = clientAddr.getIP() + ":" + std::to_string(clientAddr.getPort()) + " # " + buffer;
                    LOG(DEBUG) << clientinfo;

                    //线程池给在线用户转发
                    task_t task = std::bind(UdpServer::_router,_socket,message);
                    ThreadPoolModule::ThreadPool<task_t>::GetInstance()->Push(task);
                }
            }
        }
        _isRunning = false;
    }

private:
    int _socket;
    InetAddr _addr;
    bool _isRunning;
    addUser_t _adduser;
    router_t _router;
    removeUser_t _remove;
};
